import { Service } from 'egg';
import { col, Transaction } from 'sequelize';
import sequelize = require('sequelize');
import { v4 as uuidv4 } from 'uuid';

import {
  ANSWER_STATE,
  TOTAL_QUESTION
} from '../../constants/answer-progress-state';

class UserService extends Service {
  /**
   * 获取用户已回答的题目数量
   * @param mergeId merge-user-questionnaire表主键
   * @return {count: number} 已经回答的题目个数
   */
  async countAnsweredQuestions(mergeId: number, t: Transaction) {
    const { count } = await this.ctx.model.Answer.findAll({
      attributes: [
        [sequelize.fn('count', sequelize.col('fk_question')), 'count'],
        'FKMergeUserQuestionnaire'
      ],
      where: { FKMergeUserQuestionnaire: mergeId }
    }, { transaction: t });

    return count;
  }

  /**
   * 获取用户答题状态值
   * @param tel 用户电话
   * @param uuid 问卷uuid
   * @return { answerProgressState: number } 答题状态值
   */
  async answerProgressState(tel: string, uuid: string) {
    const result = await this.ctx.model.transaction(async (t) => {
      const existUserData = await this.ctx.model.User.findOne({
        where: { tel }
      }, { transaction: t });

      if (existUserData === null) {
        // 未验证
        return { answerProgressState: ANSWER_STATE.UNVERIFIED };
      }

      if (existUserData.complateFlag === 0) {
        // 已验证用户
        return {
          answerProgressState: ANSWER_STATE.VERIFIED,
          userUuid: existUserData.uuid
        };
      }

      const userId = existUserData.id;
      const { id: questionnaireId } = await this
        .ctx
        .model
        .Questionnaire.findOne(
          {
            where: { uuid }
          }, { transaction: t }
        );
      const merge = await this.ctx.model.MergeUserQuestionnaire.findOne({
        where: {
          FKUser: userId,
          FKQuestionnaire: questionnaireId
        },
      }, { transaction: t });

      // 用户有答题记录，但是回答的并不是本次的试题。
      if (existUserData.complateFlag === 1 && merge === null) {
        return {
          answerProgressState: ANSWER_STATE.VERIFIED,
          userUuid: existUserData.uuid
        };
      }
      const { uuid: mergeUuid } = merge;
      const answeredQuestionsCount = await this.ctx.model.Answer.count({
        include: [
          {
            model: this.ctx.model.MergeUserQuestionnaire,
            where: {
              FKUser: userId,
              FKQuestionnaire: questionnaireId
            },
          },
        ],
        attributes: [
          [sequelize.fn('count', sequelize.col('fk_question')), 'count']
        ],
      }, { transaction: t });

      const res = await this.ctx.service.answer.score(
        questionnaireId,
        userId,
        t
      );

      if (!res) {
        return {};
      }

      const { scoreList } = res;

      if (answeredQuestionsCount === 0) {
        // 有基本信息
        return {
          answerProgressState: ANSWER_STATE.HAVED_BASIC_INFO,
          mergeUuid,
          userUuid: existUserData.uuid,
          scoreList
        };
      }

      if (answeredQuestionsCount < TOTAL_QUESTION) {
        // 正在答题
        return {
          answerProgressState: ANSWER_STATE.ANSWERING,
          currentQuestion: answeredQuestionsCount,
          userUuid: existUserData.uuid,
          mergeUuid,
          scoreList
        };
      }

      if (answeredQuestionsCount === TOTAL_QUESTION) {
        // 完成答题
        return {
          answerProgressState: ANSWER_STATE.ANSWER_FINISHED,
          userUuid: existUserData.uuid,
          mergeUuid
        };
      }

      throw new Error(`${existUserData.uuid} 回答的题目数量超过了 ${TOTAL_QUESTION}`);
    });

    return result;
  }

  async userInfo(
    {
      questionnaireUuid,
      userUuid,
      name,
      num,
      email,
      gender,
      maritalStatus,
      childrenSituation,
      age,
      education,
      affiliated,
      jobPosition,
      workUnit,
      operatingPost,
      startTime
    }
  ) {
    const result = await this.ctx.model.transaction(async (t) => {
      const [existUser, existQuestionnaire] = await Promise.all([
        await this.ctx.model.User.findOne({
          where: {
            uuid: userUuid
          }
        }, { transaction: t }),
        await this.ctx.model.Questionnaire.findOne({
          where: {
            uuid: questionnaireUuid
          }
        }, { transaction: t })
      ]);

      if (!(existQuestionnaire && existUser)) {
        return false;
      }

      const [
        updateResult,
        mergeCreateResult
      ] = await Promise.all([
        await this.ctx.model.User.update({
          name,
          num,
          gender,
          maritalStatus,
          childrenSituation,
          age,
          education,
          workUnit,
          affiliated,
          jobPosition,
          operatingPost,
          complateFlag: 1
        }, {
          where: {
            uuid: userUuid
          }
        }, { transaction: t }),
        await this.ctx.model.MergeUserQuestionnaire.findOrCreate({
          where: {
            FKQuestionnaire: existQuestionnaire.id,
            FKUser: existUser.id,
          },
          defaults: {
            uuid: uuidv4(),
            startTime,
            email,
          },
          transaction: t
        })
      ]);

      if ((updateResult[0] < 1) || (!mergeCreateResult[0])) {
        return false;
      }

      const { uuid: mergeUuid } = mergeCreateResult[0].dataValues;

      return mergeUuid;
    });

    return result;
  }

  async findUserByMergeUuid(mergeUuid: string) {
    const { ctx } = this;

    const result = await ctx.model.transaction(async (t) => {
      const userInfo = await ctx.model.MergeUserQuestionnaire.findOne({
        where: { uuid: mergeUuid },
        attributes: [
          ['fk_questionnaire', 'questionnaireId'],
          'email',
          [col('User.tel'), 'tel'],
          [col('User.name'), 'name'],
          [col('User.num'), 'num'],
          [col('User.gender'), 'gender'],
          [col('User.marital_status'), 'maritalStatus'],
          [col('User.children_situation'), 'childrenSituation'],
          [col('User.age'), 'age'],
          [col('User.education'), 'education'],
          [col('User.job_position'), 'jobPosition'],
          [col('User.work_unit'), 'workUnit'],
          [col('User.affiliated'), 'affiliated'],
          [col('User.operating_post'), 'operatingPost'],
        ],
        include: [
          {
            model: ctx.model.User,
            attributes: [],
          },
        ],
        raw: true
      }, { transaction: t });

      const {
        questionnaireId, jobPosition, workUnit, affiliated, operatingPost
      } = userInfo;

      const [
        jobPositionUuidAndName,
        workUnitUuidAndName,
        affiliatedUuidAndName,
        operatingPostUuidAndName
      ] = await Promise.all([
        ctx.model.JobPosition.findOne({
          where: { fk_questionnaire: questionnaireId, name: jobPosition },
          attributes: ['uuid', 'name'],
        }, { transaction: t }),
        ctx.model.WorkUnit.findOne({
          where: { fk_questionnaire: questionnaireId, name: workUnit },
          attributes: ['uuid', 'name'],
        }, { transaction: t }),
        ctx.model.AffiliatedUnit.findOne({
          where: { fk_questionnaire: questionnaireId, name: affiliated },
          attributes: ['uuid', 'name'],
        }, { transaction: t }),
        ctx.model.OperatingPost.findOne({
          where: { fk_questionnaire: questionnaireId, name: operatingPost },
          attributes: ['uuid', 'name'],
        }, { transaction: t })
      ]);

      delete userInfo.questionnaireId;

      return {
        ...userInfo,
        jobPosition: jobPositionUuidAndName,
        workUnit: workUnitUuidAndName,
        affiliated: affiliatedUuidAndName,
        operatingPost: operatingPostUuidAndName
      };
    });

    return result;
  }

  async existTel(tel: string) {
    const { ctx } = this;

    const result = await ctx.model.User.findOne({
      where: { tel }
    });

    if (result) {
      return true;
    }
    return false;
  }
}

export default UserService;
